package com.chagee.store.operation.server.redis.service;

import com.chagee.store.operation.common.exception.BizErrorCode;
import com.chagee.store.operation.common.exception.BizException;
import com.chagee.store.operation.server.redis.model.RedissionLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author chang.jiawei
 * @date 2024/1/30 12:02
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class RedisLockServiceImpl implements RedisLockService {

	// 默认获取锁等待的时间 1s
	private static final long A_MOMENT = 1;
	// 最大的锁释放时间 10分种
	private static final long MAX_RELEASE_TIME = 600L;

	private final RedissonClient redissonClient;

	@Override
	public void lockWaitAMoment(RedissionLock redissionLock, Long waitTime) {
		if (waitTime == null || waitTime < 0) {
			waitTime = A_MOMENT;
		}
		String key = redissionLock.buildLockKey();
		RLock lock = redissonClient.getLock(key);
		boolean b;
		try {
			b = lock.tryLock(waitTime, MAX_RELEASE_TIME, TimeUnit.SECONDS);
		} catch (Exception e) {
			log.warn("获取锁失败,key={}", key, e);
			redissionLock.setSuccess(false);
			return;
		}

		if (b) {
			redissionLock.setLock(lock);
			redissionLock.setSuccess(true);
		} else {
			log.warn("加锁失败。。。。。threadname:【{}】....{}", Thread.currentThread().getName(), Instant.now().toEpochMilli());
			redissionLock.setSuccess(false);
		}
	}

	private void lock(RedissionLock redissionLock) {
		this.lockWaitAMoment(redissionLock, A_MOMENT);
	}


	@Override
	public void lockElseThrowError(List<? extends RedissionLock> redissionLocks, String errorMsg) {
		if (CollectionUtils.isEmpty(redissionLocks)) {
			return;
		}
		for (RedissionLock redissionLock : redissionLocks) {
			this.lockElseThrowError(redissionLock, errorMsg);
		}
	}

	@Override
	public void lockElseThrowError(RedissionLock redissionLock, String errorMsg) {
		if (Objects.isNull(redissionLock)) {
			return;
		}
		this.lock(redissionLock);
		if (!redissionLock.isSuccess()) {
			log.warn("加锁失败,redissionLock:{},errorMsg:{}", Objects.nonNull(redissionLock.getLock()) ? redissionLock.getLock().getName() : null, errorMsg);
			throw new BizException(BizErrorCode.PLEASE_WAIT);
		}
	}

	@Override
	public void unLock(RedissionLock redissionLock) {
		try {
			if (redissionLock != null && redissionLock.getLock() != null) {
				RLock lock = redissionLock.getLock();
				if (lock.isLocked()) {
					lock.unlock();
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("释放锁失败!");
		}
	}

	@Override
	public void unLock(List<? extends RedissionLock> redissionLocks) {
		if (CollectionUtils.isEmpty(redissionLocks)) {
			return;
		}
		for (RedissionLock redissionLock : redissionLocks) {
			this.unLock(redissionLock);
		}
	}
}
